package org.stars.dao;

import java.io.Serializable;
import java.sql.Connection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stars.daostars.DaoBase;
import org.stars.daostars.DaoBaseNewHelper;
import org.stars.daostars.QueryConfig;
import org.stars.daostars.SelectResult;
import org.stars.daostars.SelectRuntime;
import org.stars.daostars.annotation.Dao;
import org.stars.daostars.core.SqlParameter;
import org.stars.daostars.sqlmapper.SqlMapper;
import org.stars.util.StringUtility;

public class DaoBaseNew<E> implements Serializable  {

	/**
	 * logger del dao.
	 * 
	 * @return logger
	 */
	public Log getLog() {
		return LogFactory.getLog(DaoBase.class);
	}

	/**
	 * Costruttore di base. Definito solo per essere compatibile con le
	 * specifiche JavaBean
	 */
	public DaoBaseNew() {

	}
	
	
	/**
	 * Setup della classe
	 * 
	 * @param eType
	 *            tipo di entity bean gestito dal dao
	 */
	public void setUp(Class<E> eType) {
		setUp(null, eType);
	}
	
	
	protected Class<E> entityBeanClass;
	protected String entityBeanClassName;
	
 
	/**
	 * Setup della classe
	 * 
	 * @param eType
	 *            tipo di entity bean gestito dal dao
	 */
	public void setUp(String daoDefinitionName, Class<E> eType) {
		// Impostiamo il nome della classe.
		// Utile in seguito.
		entityBeanClassName = eType.getName();
		entityBeanClass = eType; // classe gestita dal dao

		// associamo dal dao le query.
		String daoClassName = "";
		Dao daoAssociation = getClass().getAnnotation(Dao.class);
		if (daoAssociation!=null && StringUtility.nvl(daoAssociation.name()).trim().length() > 0) {
			// ASSERT: il nome della definizione e' stata definita mediante
			// annotation
			daoClassName = daoAssociation.name().trim();
		} else if (StringUtility.nvl(daoDefinitionName).trim().length() > 0) {
			// ASSERT: il nome della definizione e' stata definita mediante
			// parametro del metodo
			daoClassName = daoDefinitionName.trim();
		} else {
			// ASSERT: nell'ultimo caso cerchiamo di ricavarlo direttamente dal
			// nome dell'entity bean
			daoClassName = DaoBaseNewHelper.getDaoNameFromEntity(entityBeanClass);
		}
		sqlMapper = DaoBaseNewHelper.getSqlMapper(daoClassName);
	}
	
	private static final long serialVersionUID = -7130799481636288197L;
	
	/**
	 * Definzione del dao, inteso come sql, parametri, mappe e sql condizionale.
	 */
	protected SqlMapper sqlMapper;
	
	/**
	 * Restituisce un'istanza di entity bean che specializza il dao. Il nome
	 * della classe che specializza il dao e' contenuto nella proprieta'
	 * <code>entityBeanClass</code>.
	 * <p>
	 * Nel caso di errore viene sollevata un'eccezione.
	 * 
	 * @return istanza dell'entity bean
	 */
	@SuppressWarnings("unchecked")
	protected E newBeanInstance() {
		E ret = null;
		try {
			// possiamo fare così perchè nel costruttore abbiamo definito il
			// nome della classe
			ret = (E) Class.forName(entityBeanClassName).newInstance();
		} catch (Exception e) {
			Log log = getLog();
			log.fatal("The creation of the bean " + entityBeanClassName + " is impossible. Reason: " + e.toString());
			e.printStackTrace();
		}
		return ret;
	}
	
	/**
	 * recupera i parametri di una query dato il nome di una query.
	 * 
	 * @param key
	 *            query name
	 * 
	 * @return elenco dei parametri.
	 */
	protected List<SqlParameter> getQueryParameters(String key) {
		return sqlMapper.getQueryParameters(key);
	}

	/**
	 * dato il nome di una query, recupera, se esiste, il relativo codice SQL.
	 * Se non lo trova, restituisce una stringa vuota.
	 * 
	 * @param key
	 *            query name
	 * 
	 * @return String di query. Nel caso in cui l'sql non è definito, viene
	 *         restituita una stringa vuota.
	 */
	public String getQuerySQL(String key) {
		return sqlMapper.getQuerySql(key);
	}

	public SqlMapper getSqlMapper() {
		return sqlMapper;
	}

	public void setSqlMapper(SqlMapper sqlMapper) {
		this.sqlMapper = sqlMapper;
	}
	
	public SelectResult<E> select(String queryName, Object dto) throws Exception {
		return SelectRuntime.select(this, null, null, queryName, null, dto, null, entityBeanClass);
	}
	
	public <T> SelectResult<T> select(QueryConfig config, Connection connection,  String queryName, String moreSql, Object dto, Object[] indexedParameters, Class<T> classResultType) throws Exception {
		return SelectRuntime.select(this, config, connection, queryName, moreSql, dto, indexedParameters, classResultType);
	}
}
